---------------------------------------------------------------------------------------------------------------------- -- Менеджер изменения состояния тела -- автор: Диденко Руслан (Stohe) -- TODO: -- modified by Alundaio ---------------------------------------------------------------------------------------------------------------------- --function printf() --end combat_action_ids = { -- basically if your scheme sets body/movement states manually and doesn't use set_state, then you need it here [stalker_ids.action_combat_planner] = true, [stalker_ids.action_danger_planner] = true, [stalker_ids.action_anomaly_planner] = true, [xrs_facer.actid_facer] = true, [xrs_facer.actid_steal_up_facer] = true, [xr_danger.actid] = true } no_idle_alife_ids = { [xr_actions_id.stohe_meet_base + 1] = true } local flag_once = false ------------------------------------------------------ -- Private ------------------------------------------------------ -- Should be faster than mgr.planner:action(id):evaluate() right? local function eval_movement(mgr,npc) return state_lib.states[mgr.target_state].movement == nil or state_lib.states[mgr.target_state].movement == npc:target_movement_type() and not npc:is_body_turning() end local function eval_animstate_locked(mgr) return mgr.animstate.states.anim_marker ~= nil and mgr.animstate.states.anim_marker ~= 3 end local function eval_animation_locked(mgr) return mgr.animation.states.anim_marker ~= nil end local function eval_animation(mgr,npc) return state_lib.states[mgr.target_state].animation == mgr.animation.states.current_state end local function eval_animstate(mgr,npc) return state_lib.states[mgr.target_state].animstate == mgr.animstate.states.current_state end local function eval_smart_cover(mgr,npc) if mgr.target_state ~= "smartcover" then return true end local st = db.storage[npc:id()] local state_descr = st and st["smartcover"] local dest_smart_cover_name = state_descr and npc:get_dest_smart_cover_name() if not dest_smart_cover_name then return true end return dest_smart_cover_name == (state_descr.cover_name or "") end local function eval_smart_cover_locked(mgr,npc) local st = db.storage[npc:id()] local state_descr = st and st["smartcover"] if state_descr == nil then return false end local in_smart_cover = npc:in_smart_cover() return (in_smart_cover and state_descr.cover_name == nil) or (not in_smart_cover and state_descr.cover_name ~= nil) end --' Эвалуатор, который синхронизирует менеджер. --' Мы уже в безраличном состоянии idle или нет class "evaluator_state_mgr_idle" (property_evaluator) function evaluator_state_mgr_idle:__init(name, state_manager) super (nil, name) self.st = state_manager end function evaluator_state_mgr_idle:evaluate() --utils_data.debug_write("eva_state_mgr_idle") if not self.object:alive() then return true end self.st.mgr = self.st.mgr or self.object:motivation_action_manager() if not (self.st.mgr and self.st.mgr:initialized()) then return false end local cid = self.st.mgr:current_action_id() if (combat_action_ids[cid] ~= true) then self.st.combat = false end if (self.st.combat == true) then return true end if (self.st.target_state == "idle" and cid == xr_actions_id.state_mgr + 1) then if not (eval_animstate_locked(self.st,self.object) and eval_animation_locked(self.st,self.object)) then if (eval_movement(self.st,self.object) and eval_animstate(self.st,self.object) and eval_animation(self.st,self.object) and eval_smart_cover(self.st,self.object)) then self.st.combat = true return true end end end return false end --' Мы уже в безраличном состоянии idle или нет ()учет с проверкой alife class "evaluator_state_mgr_idle_alife" (property_evaluator) function evaluator_state_mgr_idle_alife:__init(name, state_manager) super (nil, name) self.st = state_manager end function evaluator_state_mgr_idle_alife:evaluate() --utils_data.debug_write("eva_state_mgr_idle_alife") if not self.object:alive() then return true end local mgr = self.object:motivation_action_manager() if not (mgr and mgr:initialized()) then return false end local cid = mgr:current_action_id() if (cid ~= xr_actions_id.alife) then self.st.alife = false end if (no_idle_alife_ids[cid] == true) then return false end if (self.st.alife == true) then return true end if (self.st.target_state == "idle" and cid == xr_actions_id.state_mgr + 2) then if not (eval_animstate_locked(self.st,self.object) and eval_animation_locked(self.st,self.object)) then if (eval_movement(self.st,self.object) and eval_animstate(self.st,self.object) and eval_animation(self.st,self.object) and eval_smart_cover(self.st,self.object)) then self.st.alife = true return true end end end return false end --' Мы уже в безраличном состоянии idle или нет ()учет с проверкой alife class "evaluator_state_mgr_idle_items" (property_evaluator) function evaluator_state_mgr_idle_items:__init(name, state_manager) super (nil, name) self.st = state_manager end function evaluator_state_mgr_idle_items:evaluate() if not self.object:alive() then return true end if (no_idle_alife_ids[cid] == true) then return false end if (self.st.target_state == "idle") then if not (eval_animstate_locked(self.st,self.object) and eval_animation_locked(self.st,self.object)) then if (eval_movement(self.st,self.object) and eval_animstate(self.st,self.object) and eval_animation(self.st,self.object) and eval_smart_cover(self.st,self.object)) then return true end end end return false end class "evaluator_state_mgr_logic_active" (property_evaluator) function evaluator_state_mgr_logic_active:__init(name, state_manager) super (nil, name) self.st = state_manager end function evaluator_state_mgr_logic_active:evaluate() return db.storage[self.object:id()] and db.storage[self.object:id()].active_section ~= nil or false end --' Переход в idle class "act_state_mgr_to_idle" (action_base) function act_state_mgr_to_idle:__init(name,state_manager) super (nil, name) self.st = state_manager end function act_state_mgr_to_idle:initialize() action_base.initialize(self) self.object:inactualize_patrol_path() self.object:set_path_type(game_object.level_path) utils_obj.send_to_nearest_accessible_vertex( self.object, self.object:level_vertex_id(), "state_mgr_to_idle" ) end function act_state_mgr_to_idle:execute() --callstack() --utils_data.debug_write(strformat("act_state_mgr_to_idle:execute %s",self.object:name())) action_base.execute(self) if (self.st.target_state ~= "idle") then self.object:clear_animations() self.st:set_state("idle", nil, nil, nil, {fast_set = true}) self.st.animation:set_state(nil, true) self.st.animation:set_control() end end function act_state_mgr_to_idle:finalize() action_base.finalize(self) end --'------------------------------------------------------------------------------------- --' Эвалуаторы и экшены менеджера --'------------------------------------------------------------------------------------- --' Закончил ли менеджер свою работу class "eva_state_mgr_end" (property_evaluator) function eva_state_mgr_end:__init(name, st) super (nil, name) self.st = st end function eva_state_mgr_end:evaluate() --utils_data.debug_write("eva_state_mgr_end") if not (self.object) then callstack() return false end --callstack() local mgr = self.object:motivation_action_manager() if not (mgr and mgr:initialized()) then return false end local cid = mgr:current_action_id() if (combat_action_ids[cid] ~= true) then self.st.combat = false end if (cid ~= xr_actions_id.alife) then self.st.alife = false end return false end --' Залочен ли менеджер class "eva_state_mgr_locked" (property_evaluator) function eva_state_mgr_locked:__init(name, st) super (nil, name) self.st = st end function eva_state_mgr_locked:evaluate() --utils_data.debug_write("eva_state_mgr_locked") if (state_mgr_weapon.weapon_locked(self.object)) then return true end if (self.object:is_body_turning()) then return true end return false end class "eva_state_mgr_locked_external" (property_evaluator) function eva_state_mgr_locked_external:__init(name, st) super (nil, name) self.st = st end function eva_state_mgr_locked_external:evaluate() if (self.st.combat or self.st.alife) then return true end return false end --' Идловый экшн менеджера class "act_state_mgr_end" (action_base) function act_state_mgr_end:__init(name, st) super (nil, name) self.st = st end function act_state_mgr_end:initialize() action_base.initialize(self) end function act_state_mgr_end:execute() action_base.execute(self) --utils_data.debug_write(strformat("act_state_mgr_end:weapon_update %s",self.object:name())) if self.st.callback and self.st.callback.timeout then if self.st.callback.begin == nil then self.st.callback.begin = time_global() end if time_global() - self.st.callback.begin >= self.st.callback.timeout then if self.st.callback.func ~= nil then self.st.callback.func(self.st.callback.obj) end self.st.callback = nil end end local t = state_lib.states[self.st.target_state].weapon local wpn = self.object:best_weapon() if not IsWeapon(wpn) then return end if (t == "sniper_fire" or t == "fire") then -- local o = self.st.look_object and db.storage[self.st.look_object] and db.storage[self.st.look_object].object -- if (o == nil or o:alive()) then if (self.st.planner:evaluator(state_mgr_goap.properties["direction"]):evaluate()) then if (t == "sniper_fire") then local aim = math.random(500,1500) self.st.npc:set_item(object.fire1, wpn, 1, aim) else self.st.npc:set_item(object.fire1, wpn, state_mgr_weapon.get_queue_params(self.st.npc, state_lib.states[self.st.target_state], {5,300,0} )) end end -- else -- object is dead -- self.st.npc:set_item(state_mgr_weapon.get_idle_state(self.st.target_state), wpn) -- end elseif (t == "unstrapped") then self.st.npc:set_item(state_mgr_weapon.get_idle_state(self.st.target_state), wpn) end end function act_state_mgr_end:finalize() action_base.finalize(self) end --' Лок менеджера class "act_state_mgr_locked" (action_base) function act_state_mgr_locked:__init(name, st) super (nil, name) self.st = st end function act_state_mgr_locked:initialize() action_base.initialize(self) end function act_state_mgr_locked:execute() action_base.execute(self) end function act_state_mgr_locked:finalize() action_base.finalize(self) end --' Сам менеджер class "state_manager" function state_manager:__init(npc) state_mgr_goap.goap_graph(self, npc) self.target_state = "idle" self.current_direction = nil self.target_position = nil self.current_object = nil self.combat = false self.alife = true self.need_reweapon = false end function state_manager:set_state(state_name, callback, timeout, target, extra) if (DEV_DEBUG and db.storage[self.npc:id()].debug_state) then state_name = db.storage[self.npc:id()].debug_state end --utils_data.debug_write("state_manager:set_state "..state_name) if state_lib.states[state_name] == nil then if (not flag_once) then flag_once = true callstack() end printf("!ERROR: ILLEGAL SET STATE CALLED!!! %s for %s", tostring(state_name), self.npc:name()) --TODO: find out why some states are nil return end self.look_position = target and target.look_position or nil self.look_object = target and target.look_object and (type(target.look_object) == "number" and target.look_object or target.look_object.id and target.look_object:id()) or nil self.look_dir = target and target.look_dir or nil local switched = false local last_state = self.target_state if (self.target_state == state_name) then return end self.current_object = nil if (state_lib.states[self.target_state].weapon == "fire" or state_lib.states[self.target_state].weapon == "sniper_fire") then if (state_lib.states[state_name].weapon ~= "fire" and state_lib.states[state_name].weapon ~= "sniper_fire") then if (self.npc:active_item() and self.npc:best_weapon() and self.npc:weapon_unstrapped()) then self.npc:set_item(object.idle, state_mgr_weapon.get_weapon(self.npc, state_name)) end end end self.target_state = state_name local danger_move = state_lib.states[state_name].special_danger_move ~= nil or false if (self.npc:special_danger_move() ~= danger_move) then self.npc:special_danger_move(danger_move) end if (extra) then self.fast_set = extra.fast_set if (self.animation_position and extra.animation_position and not utils_data.vector_cmp(self.animation_position,extra.animation_position)) then self.animation_direction_applied = nil elseif (self.animation_direction and extra.animation_direction and not utils_data.vector_cmp(self.animation_direction,extra.animation_direction)) then self.animation_direction_applied = nil end self.animation_position = extra.animation_position self.animation_direction = extra.animation_direction else self.animation_direction_applied = nil self.animation_position = nil self.animation_direction = nil self.fast_set = nil end if (callback) then self.callback = callback if (timeout and timeout >= 0) then self.callback.timeout = timeout self.callback.begin = nil else self.callback.func = nil self.callback.timeout = nil end end end function state_manager:get_state() return self.target_state end function state_manager:update() if (self.callback and self.callback.func and self.animation.states.current_state == state_lib.states[self.target_state].animation) then local tg = time_global() if not (self.callback.begin) then self.callback.begin = tg else if (tg - self.callback.begin >= self.callback.timeout) then self.callback.func(self.callback.obj) self.callback.begin = nil self.callback.func = nil end end end --utils_data.debug_write("\n-----STATE MANAGER UPDATE----",true) --printf("state_manager:update() %s",self.npc:name()) self.planner:update() if not (self.planner:initialized()) then return end --- DEBUG PLANNER --[[ local manager = self.npc:motivation_action_manager() local id if (manager and manager:initialized()) then id = manager:current_action_id() if (id == stalker_ids.action_alife_planner) then local alife_action = manager:action(stalker_ids.action_alife_planner) local alife_action_planner = cast_planner(alife_action) id = alife_action_planner:current_action_id() elseif (id == stalker_ids.action_combat_planner) then local combat_action = manager:action(stalker_ids.action_combat_planner) local combat_action_planner = cast_planner(combat_action) id = combat_action_planner:current_action_id() elseif (id == stalker_ids.action_danger_planner) then local danger_action = manager:action(stalker_ids.action_danger_planner) local danger_action_planner = cast_planner(danger_action) id = danger_action_planner:current_action_id() end end local gid = self.planner:current_action_id() --]] --utils_data.debug_write(strformat("goap_action = %s current_action = %s",gid,(id and xrs_debug_tools.actions_by_stalker_ids[id] or id))) -- END DEBUG PLANNER local lid local id = self.planner:current_action_id() while id and id ~= lid and id ~= state_mgr_goap.operators["end"] and id ~= state_mgr_goap.operators["locked"] do --utils_data.debug_write("current_action_id = "..(xrs_debug_tools.actions_by_stalker_ids[id] or id),true) self.planner:update() lid = id id = self.planner:current_action_id() end --utils_data.debug_write("--------------state_mgr:update end------------------") end function set_state(npc, state_name, callback, timeout, target, extra) local id = npc:id() local st = db.storage[id] if not (st) then return end if st.state_mgr and state_name then st.state_mgr:set_state(state_name, callback, timeout, target, extra) end end function get_state(npc) local id = npc:id() local st = db.storage[id] if not (st) then return end if st.state_mgr then return st.state_mgr:get_state() end return nil end function bind_manager(object) if not object then return end --[[ local properties = {} properties["state_mgr_idle_combat"] = xr_evaluators_id.state_mgr + 1 properties["state_mgr_idle_alife"] = xr_evaluators_id.state_mgr + 2 properties["state_mgr_idle_smartcover"] = xr_evaluators_id.state_mgr + 3 properties["state_mgr_logic_active"] = xr_evaluators_id.state_mgr + 4 properties["state_mgr_idle_items"] = xr_evaluators_id.state_mgr + 5 local operators = {} operators["state_mgr_to_idle_combat"] = xr_actions_id.state_mgr + 1 operators["state_mgr_to_idle_alife"] = xr_actions_id.state_mgr + 2 operators["state_mgr_to_idle_items"] = xr_actions_id.state_mgr + 3 --]] local manager = object:motivation_action_manager() if not manager then return end local state_manager = state_mgr.state_manager(object) manager:add_evaluator(xr_evaluators_id.state_mgr + 1, evaluator_state_mgr_idle("state_mgr_idle_combat", state_manager)) manager:add_evaluator(xr_evaluators_id.state_mgr + 2, evaluator_state_mgr_idle_alife("state_mgr_idle_alife", state_manager)) manager:add_evaluator(xr_evaluators_id.state_mgr + 4, evaluator_state_mgr_logic_active("state_mgr_logic_active", state_manager)) manager:add_evaluator(xr_evaluators_id.state_mgr + 5, evaluator_state_mgr_idle_items("state_mgr_idle_items", state_manager)) local wp = world_property local action = this.act_state_mgr_to_idle("state_mgr_to_idle_combat", state_manager) if action then action:add_precondition (wp(stalker_ids.property_alive, true)) action:add_precondition (wp(xr_evaluators_id.state_mgr + 1, false)) action:add_effect (wp(xr_evaluators_id.state_mgr + 1, true)) manager:add_action(xr_actions_id.state_mgr + 1, action) end --[[ action = this.act_state_mgr_to_idle("state_mgr_to_idle_items", state_manager) if action then action:add_precondition (wp(xr_evaluators_id.state_mgr + 5, false)) action:add_precondition (wp(stalker_ids.property_enemy, false)) action:add_precondition (wp(stalker_ids.property_alive, true)) action:add_effect (wp(xr_evaluators_id.state_mgr + 5, true)) manager:add_action(xr_actions_id.state_mgr + 3, action) end --]] action = this.act_state_mgr_to_idle("state_mgr_to_idle_alife", state_manager) if action then action:add_precondition (wp(stalker_ids.property_alive, true)) action:add_precondition (wp(stalker_ids.property_enemy, false)) action:add_precondition (wp(stalker_ids.property_danger,false)) action:add_precondition (wp(xr_evaluators_id.state_mgr + 4, false)) action:add_precondition (wp(xr_evaluators_id.state_mgr + 2, false)) action:add_effect (wp(xr_evaluators_id.state_mgr + 2, true)) manager:add_action(xr_actions_id.state_mgr + 2, action) end --printf("ACTION_ALIFE_ID(state_mgr.bind_manager): " .. tostring(xr_actions_id.alife)) action = manager:action(xr_actions_id.alife) if action then action:add_precondition(wp(xr_evaluators_id.state_mgr + 2,true)) end --[[ action = manager:action(stalker_ids.action_gather_items) if action then action:add_precondition(wp(xr_evaluators_id.state_mgr + 5,true)) end --]] action = manager:action(stalker_ids.action_combat_planner) if action then action:add_precondition(wp(xr_evaluators_id.state_mgr + 1,true)) end local combat_action_planner = cast_planner(action) if combat_action_planner then combat_action_planner:remove_action(stalker_ids.action_kill_enemy_if_not_visible) end action = manager:action(stalker_ids.action_anomaly_planner) if action then action:add_precondition(wp(xr_evaluators_id.state_mgr + 1,true)) end --local anomaly_action_planner = cast_planner(action) --if anomaly_action_planner then anomaly_action_planner:remove_action(stalker_ids.detect_anomaly) end action = manager:action(stalker_ids.action_danger_planner) if action then action:add_precondition(wp(xr_evaluators_id.state_mgr + 1,true)) end return state_manager end function is_npc_in_combat(npc) local mgr = npc:motivation_action_manager() if not mgr or not mgr:initialized() then return false end local current_action_id = mgr:current_action_id() return current_action_id == stalker_ids.action_combat_planner end